{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "linear.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "private_outputs": true,
      "collapsed_sections": [],
      "toc_visible": true
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.7.1"
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "OoasdhSAp0zJ"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "form",
        "colab_type": "code",
        "id": "cIrwotvGqsYh",
        "colab": {}
      },
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License.\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "C81KT2D_j-xR"
      },
      "source": [
        "# 추정기(Estimator)로 선형 모델 만들기\n",
        "\n",
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/estimator/linear\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />TensorFlow.org에서 보기</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ko/tutorials/estimator/linear.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />구글 코랩(Colab)에서 실행하기</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ko/tutorials/estimator/linear.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />깃허브(GitHub) 소스 보기</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "tUP8LMdYtWPz"
      },
      "source": [
        "Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도 불구하고 [공식 영문 문서](https://github.com/tensorflow/docs/blob/master/site/en/guide/gpu.ipynb)의 내용과 일치하지 않을 수 있습니다. 이 번역에 개선할 부분이 있다면 [tensorflow/docs](https://github.com/tensorflow/docs) 깃헙 저장소로 풀 리퀘스트를 보내주시기 바랍니다. 문서 번역이나 리뷰에 참여하려면 [docs-ko@tensorflow.org](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ko)로 메일을 보내주시기 바랍니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "nNgxmCgx8aAM"
      },
      "source": [
        "## 개요\n",
        "\n",
        "이 문서에서는 `tf.estimator` API를 사용하여 로지스틱 회귀 모델(logistic regression model)을 훈련합니다. 이 모델은 다른 더 복잡한 알고리즘의 기초로 사용할 수 있습니다.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "vkC_j6VpqrDw"
      },
      "source": [
        "## 설정"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "rutbJGmpqvm3",
        "colab": {}
      },
      "source": [
        "!pip install sklearn"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "54mb4J9PqqDh",
        "colab": {}
      },
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals\n",
        "\n",
        "import os\n",
        "import sys\n",
        "\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "import matplotlib.pyplot as plt\n",
        "from IPython.display import clear_output\n",
        "from six.moves import urllib"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "fsjkwfsGOBMT"
      },
      "source": [
        "## 타이타닉 데이터셋을 불러오기\n",
        "타이타닉 데이터셋을 사용할 것입니다. 성별, 나이, 클래스, 기타 등 주어진 정보를 활용하여 승객이 살아남을 것인지 예측하는 것을 목표로 합니다."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "bNiwh-APcRVD",
        "colab": {}
      },
      "source": [
        "try:\n",
        "  # Colab only\n",
        "  %tensorflow_version 2.x\n",
        "except Exception:\n",
        "    pass\n",
        "import tensorflow.compat.v2.feature_column as fc\n",
        "\n",
        "import tensorflow as tf"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "DSeMKcx03d5R",
        "colab": {}
      },
      "source": [
        "# 데이터셋 불러오기.\n",
        "dftrain = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/train.csv')\n",
        "dfeval = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/eval.csv')\n",
        "y_train = dftrain.pop('survived')\n",
        "y_eval = dfeval.pop('survived')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "jjm4Qj0u7_cp"
      },
      "source": [
        "## 데이터 탐험하기"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "UrQzxKKh4d6u"
      },
      "source": [
        "데이터셋은 다음의 특성을 가집니다"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "rTjugo3n308g",
        "colab": {}
      },
      "source": [
        "dftrain.head()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "y86q1fj44lZs",
        "colab": {}
      },
      "source": [
        "dftrain.describe()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "8JSa_duD4tFZ"
      },
      "source": [
        "훈련셋은 627개의 샘플로 평가셋은 264개의 샘플로 구성되어 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Fs3Nu5pV4v5J",
        "colab": {}
      },
      "source": [
        "dftrain.shape[0], dfeval.shape[0]"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "RxCA4Nr45AfF"
      },
      "source": [
        "대부분의 승객은 20대와 30대 입니다."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "RYeCMm7K40ZN",
        "colab": {}
      },
      "source": [
        "dftrain.age.hist(bins=20)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "DItSwJ_B5B0f"
      },
      "source": [
        "남자 승객이 여자 승객보다 대략 2배 많습니다."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "b03dVV9q5Dv2",
        "colab": {}
      },
      "source": [
        "dftrain.sex.value_counts().plot(kind='barh')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "rK6WQ29q5Jf5"
      },
      "source": [
        "대부분의 승객은 \"삼등석\" 입니다."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "dgpJVeCq5Fgd",
        "colab": {}
      },
      "source": [
        "dftrain['class'].value_counts().plot(kind='barh')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "FXJhGGL85TLp"
      },
      "source": [
        "여자는 남자보다 살아남을 확률이 훨씬 높습니다. 이는 명확하게 모델에 유용한 특성입니다."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "lSZYa7c45Ttt",
        "colab": {}
      },
      "source": [
        "pd.concat([dftrain, y_train], axis=1).groupby('sex').survived.mean().plot(kind='barh').set_xlabel('% survive')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "VqDKQLZn8L-B"
      },
      "source": [
        "## 모델을 위한 특성 공학(feature engineering)\n",
        "추정기는 [특성 열(feature columns)](https://www.tensorflow.org/guide/feature_columns)이라는 시스템을 사용하여 모델이 각각의 입력 특성을 어떻게 해석할지 설명합니다. 추정기가 숫자 입력 벡터를 요구하면, *특성 열*은 모델이 어떻게 각 특성을 변환해야하는지 설명합니다.\n",
        "\n",
        "효과적인 모델 학습에서는 적절한 특성 열을 고르고 다듬는 것이 키포인트 입니다. 하나의 특성 열은 특성 딕셔너리(dict)의 원본 입력으로 만들어진 열(*기본 특성 열*)이거나 하나 이상의 기본 열(*얻어진 특성 열*)에 정의된 변환을 이용하여 새로 생성된 열입니다.\n",
        "\n",
        "선형 추정기는 수치형, 범주형 특성을 모두 사용할 수 있습니다. 특성 열은 모든 텐서플로 추정기와 함께 작동하고 목적은 모델링에 사용되는 특성들을 정의하는 것입니다. 또한 원-핫-인코딩(one-hot-encoding), 정규화(normalization), 버킷화(bucketization)와 같은 특성 공학 방법을 지원합니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "puZFOhTDkblt"
      },
      "source": [
        "### 기본 특성 열"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "GpveXYSsADS6",
        "colab": {}
      },
      "source": [
        "CATEGORICAL_COLUMNS = ['sex', 'n_siblings_spouses', 'parch', 'class', 'deck',\n",
        "                       'embark_town', 'alone']\n",
        "NUMERIC_COLUMNS = ['age', 'fare']\n",
        "\n",
        "feature_columns = []\n",
        "for feature_name in CATEGORICAL_COLUMNS:\n",
        "  vocabulary = dftrain[feature_name].unique()\n",
        "  feature_columns.append(tf.feature_column.categorical_column_with_vocabulary_list(feature_name, vocabulary))\n",
        "\n",
        "for feature_name in NUMERIC_COLUMNS:\n",
        "  feature_columns.append(tf.feature_column.numeric_column(feature_name, dtype=tf.float32))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Gt8HMtwOh9lJ"
      },
      "source": [
        "`input_function`은 입력 파이프라인을 스트리밍으로 공급하는 `tf.data.Dataset`으로 데이터를 변환하는 방법을 명시합니다. `tf.data.Dataset`은 데이터 프레임, CSV 형식 파일 등과 같은 여러 소스를 사용합니다."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "qVtrIHFnAe7w",
        "colab": {}
      },
      "source": [
        "def make_input_fn(data_df, label_df, num_epochs=10, shuffle=True, batch_size=32):\n",
        "  def input_function():\n",
        "    ds = tf.data.Dataset.from_tensor_slices((dict(data_df), label_df))\n",
        "    if shuffle:\n",
        "      ds = ds.shuffle(1000)\n",
        "    ds = ds.batch(batch_size).repeat(num_epochs)\n",
        "    return ds\n",
        "  return input_function\n",
        "\n",
        "train_input_fn = make_input_fn(dftrain, y_train)\n",
        "eval_input_fn = make_input_fn(dfeval, y_eval, num_epochs=1, shuffle=False)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "P7UMVkQnkrgb"
      },
      "source": [
        "다음과 같이 데이터셋을 점검할 수 있습니다:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "8ZcG_3KiCb1M",
        "colab": {}
      },
      "source": [
        "ds = make_input_fn(dftrain, y_train, batch_size=10)()\n",
        "for feature_batch, label_batch in ds.take(1):\n",
        "  print('특성 키:', list(feature_batch.keys()))\n",
        "  print()\n",
        "  print('클래스 배치:', feature_batch['class'].numpy())\n",
        "  print()\n",
        "  print('레이블 배치:', label_batch.numpy())"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "lMNBMyodjlW3"
      },
      "source": [
        "또한 `tf.keras.layers.DenseFeatures` 층을 사용하여 특정한 특성 열의 결과를 점검할 수 있습니다:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "IMjlmbPlDmkB",
        "colab": {}
      },
      "source": [
        "age_column = feature_columns[7]\n",
        "tf.keras.layers.DenseFeatures([age_column])(feature_batch).numpy()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "f4zrAdCIjr3s"
      },
      "source": [
        "`DenseFeatures`는 조밀한(dense) 텐서만 허용합니다. 범주형 데이터를 점검하려면 우선 범주형 열에 indicator_column 함수를 적용해야 합니다:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "1VXmXFTSFEvv",
        "colab": {}
      },
      "source": [
        "gender_column = feature_columns[0]\n",
        "tf.keras.layers.DenseFeatures([tf.feature_column.indicator_column(gender_column)])(feature_batch).numpy()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "MEp59g5UkHYY"
      },
      "source": [
        "모든 기본 특성을 모델에 추가한 다음에 모델을 훈련해 봅시다. 모델을 훈련하려면 `tf.estimator` API를 이용한 메서드 호출 한번이면 충분합니다:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "aGXjdnqqdgIs",
        "colab": {}
      },
      "source": [
        "linear_est = tf.estimator.LinearClassifier(feature_columns=feature_columns)\n",
        "linear_est.train(train_input_fn)\n",
        "result = linear_est.evaluate(eval_input_fn)\n",
        "\n",
        "clear_output()\n",
        "print(result)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "3tOan4hDsG6d"
      },
      "source": [
        "### 도출된 특성 열"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "NOG2FSTHlAMu"
      },
      "source": [
        "이제 정확도 75%에 도달했습니다. 별도로 각 기본 특성 열을 사용하면 데이터를 설명하기에는 충분치 않을 수 있습니다. 예를 들면, 성별과 레이블간의 상관관계는 성별에 따라 다를 수 있습니다. 따라서 `gender=\"Male\"`과 'gender=\"Female\"`의 단일 모델가중치만 배우면 모든 나이-성별 조합(이를테면 `gender=\"Male\" 그리고 'age=\"30\"` 그리고 `gender=\"Male\"` 그리고 `age=\"40\"`을 구별하는 것)을 포함시킬 수 없습니다.\n",
        "\n",
        "서로 다른 특성 조합들 간의 차이를 학습하기 위해서 모델에 *교차 특성 열*을 추가할 수 있습니다(또한 교차 열 이전에 나이 열을 버킷화할 수 있습니다):"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "AM-RsDzNfGlu",
        "colab": {}
      },
      "source": [
        "age_x_gender = tf.feature_column.crossed_column(['age', 'sex'], hash_bucket_size=100)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "DqDFyPKQmGTN"
      },
      "source": [
        "조합 특성을 모델에 추가하고 모델을 다시 훈련합니다:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "s8FV9oPQfS-g",
        "colab": {}
      },
      "source": [
        "derived_feature_columns = [age_x_gender]\n",
        "linear_est = tf.estimator.LinearClassifier(feature_columns=feature_columns+derived_feature_columns)\n",
        "linear_est.train(train_input_fn)\n",
        "result = linear_est.evaluate(eval_input_fn)\n",
        "\n",
        "clear_output()\n",
        "print(result)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "rwfdZj7ImLwb"
      },
      "source": [
        "이제 정확도 77.6%에 도달했습니다. 기본 특성만 이용한 학습보다는 약간 더 좋았습니다. 더 많은 특성과 변환을 사용해서 더 잘할 수 있다는 것을 보여주세요!"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "8_eyb9d-ncjH"
      },
      "source": [
        "이제 훈련 모델을 이용해서 평가셋에서 승객에 대해 예측을 할 수 있습니다. 텐서플로 모델은 한번에 샘플의 배치 또는 일부에 대한 예측을 하도록 최적화되어있습니다. 앞서, `eval_input_fn`은 모든 평가셋을 사용하도록 정의되어 있었습니다."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "wiScyBcef6Dq",
        "colab": {}
      },
      "source": [
        "pred_dicts = list(linear_est.predict(eval_input_fn))\n",
        "probs = pd.Series([pred['probabilities'][1] for pred in pred_dicts])\n",
        "\n",
        "probs.plot(kind='hist', bins=20, title='예측 확률')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "UEHRCd4sqrLs"
      },
      "source": [
        "마지막으로, 수신자 조작 특성(receiver operating characteristic, ROC)을 살펴보면 정탐률(true positive rate)과 오탐률(false positive rate)의 상충관계에 대해 더 잘 이해할 수 있습니다. "
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "kqEjsezIokIe",
        "colab": {}
      },
      "source": [
        "from sklearn.metrics import roc_curve\n",
        "from matplotlib import pyplot as plt\n",
        "\n",
        "fpr, tpr, _ = roc_curve(y_eval, probs)\n",
        "plt.plot(fpr, tpr)\n",
        "plt.title('ROC curve')\n",
        "plt.xlabel('오탐률(false positive rate)')\n",
        "plt.ylabel('정탐률(true positive rate)')\n",
        "plt.xlim(0,)\n",
        "plt.ylim(0,)"
      ],
      "execution_count": 0,
      "outputs": []
    }
  ]
}
